/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/


const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

/**
 * 自定义promise类
 */
class MyPromise {
	// 构造函数，传入一个回调函数，表示执行器
	constructor(executor) {
		try{
			// 执行器立即执行，执行器有两个参数，一个成功回调函数，一个失败回调函数
			executor(this.resovle, this.reject)
		}catch(e){
			this.reject(e)
		}
	}
	
	// promise状态
	status = PENDING
	// 成功之后的值
	value = undefined
	// 失败之后的原因
	reason = undefined
	// 成功之后的回调
	successCallback = []
	// 失败之后的回调
	failCallback = []
	
	// 定义成功执行的方法
	resovle = value => {
		// 如果状态不是等待 阻止程序向下执行
		if (this.status !== PENDING) return
		// 将状态更改为成功
		this.status = FULFILLED
		// 保存成功之后的值
		this.value = value
		// 异步情况下 如果成功回调存在 则回调它
		while(this.successCallback.length) this.successCallback.shift()()
	}
	
	// 定义失败执行的方法
	reject = reason => {
		// 如果状态不是等待 阻止程序向下执行
		if (this.status !== PENDING) return
		// 将状态更改为失败
		this.status = REJECTED
		// 保存失败之后的原因
		this.reason = reason
		// 异步情况下 如果失败回调存在 则回调它
		while (this.failCallback.length) this.failCallback.shift()()
	}
	
	// 定义then方法
	then = (successCallback, failCallback) => {
		// 处理then方法没有传参数的情况
		successCallback = successCallback ? successCallback : value => value
		failCallback = failCallback ? failCallback : reason => { throw reason }
		// 创建一个新的promise
		let promise2 = new MyPromise((resolve, reject) => {
			// 判断状态
			if (this.status === FULFILLED) {
				// 一定要使用箭头函数，否则无法获取到this的值
				setTimeout(() => {
					try{
						let x = successCallback(this.value)
						// 判断成功回调函数的返回结果 如果是普通值 则直接调用resolve
						// 如果是promise类型，让promise自动调用resolve回调函数和reject回调函数
						resolvePromise(promise2, x, resolve, reject)
					}catch(e){
						reject(e)
					}
				}, 0)
				
			} else if (this.status === REJECTED) {
				// 一定要使用箭头函数，否则无法获取到this的值
				setTimeout(() => {
					try{
						let x = failCallback(this.reason)
						// 判断成功回调函数的返回结果 如果是普通值 则直接调用resolve
						// 如果是promise类型，让promise自动调用resolve回调函数和reject回调函数
						resolvePromise(promise2, x, resolve, reject)
					}catch(e){
						reject(e)
					}
				}, 0)
				
			} else {
				// 异步情况会进入到 等待状态
				// 将成功回调和失败回调存储起来
				this.successCallback.push(() => {
					setTimeout(() => {
						try{
							let x = successCallback(this.value)
							// 判断成功回调函数的返回结果 如果是普通值 则直接调用resolve
							// 如果是promise类型，让promise自动调用resolve回调函数和reject回调函数
							resolvePromise(promise2, x, resolve, reject)
						}catch(e){
							reject(e)
						}
					}, 0)
				})
				this.failCallback.push(() => {
					setTimeout(() => {
						try{
							let x = failCallback(this.reason)
							// 判断成功回调函数的返回结果 如果是普通值 则直接调用resolve
							// 如果是promise类型，让promise自动调用resolve回调函数和reject回调函数
							resolvePromise(promise2, x, resolve, reject)
						}catch(e){
							reject(e)
						}
					}, 0)
				})
			}
		})
		return promise2
	}
	
	// 定义finally方法
	finally (callback) {
		// 调用then方法，确保成功和失败都能执行callback回调函数
		// 为了实现链式调用，return这个调用then方法调promise对象
		return this.then(value => {
			return MyPromise.resolve(callback()).then(() => {
				return value
			})
		}, reason => {
			return MyPromise.resolve(callback()).then(() => {throw reason})
		})
	}
	
	// 定义catch方法
	catch (callback) {
		// 由于当前promise的状态是 REJECTED，所以then方法里会执行第二个回调函数，即callback
		// return当前promise
		return this.then(undefined, callback)
	}
	
	// 定义静态all方法
	static all = array => {
		let result = []
		let index = 0
		return new MyPromise((resolve, reject) => {
			// 遍历数组，将结果存储到result数组中
			// 当数组遍历完，并且每个item都执行完毕，则再把result数组传递到MyPromise.all中return出来的promise实例中存储
			function addData(key, value) {
				result[key] = value
				index++
				if (index === array.length) {
					resolve(result)
				}
			}
			for (let i = 0; i < array.length; i++) {
				let item = array[i]
				if (item instanceof MyPromise) {
					// promise类型
					item.then(value => addData(i, value), reason => reject(reason))
				} else {
					// 普通类型
					addData(i, item)
				}
			}
		})
	}
	
	// 定义静态方法resolve
	static resolve (value) {
		// 如果value是promise类型 直接返回它
		if (value instanceof MyPromise) return value
		// 如果value是普通类型，创建一个新的promise，并把value传递给promise存储起来
		return new MyPromise(resolve => resolve(value))
	}
}

// 解析promise
function resolvePromise(promise2, x, resolve, reject) {
	// 识别到自己调用自己
	if (promise2 === x) {
		return reject(new TypeError('发生错误：不能自己调用自己'))
	}
	if (x instanceof MyPromise) {
		// 是promise对象
		x.then(resolve, reject)
	} else {
		// 是普通值
		resolve(x)
	}
}

module.exports = MyPromise